30. inline Function

inline Function
인라인 함수는 함수처럼 보이고 함수처럼 동작하는데다가
메크로보다 훨씬 안전하게 사용이 가능하다.(컴파일러가 함수로 다룸)
함수를 호출할 때 소모되는 오버헤드도 없다.

일반적으로 컴파일러 최적화는 함수 호출이 없는 코드가 연속적으로 이어지는 구간에 적용되도록 설계되어 있다.
따라서 인라인 함수를 사용하면 컴파일러가 함수 본문에 대해 문맥별 최적화(context-specific)를 걸기가
용이해진다.
(컴파일러는 아웃라인(outline)함수 호출에 대해 최적화를 적용하지 못함)

하지만, 인라인 함수는 프로그램의 목적코드의 크기를 크게한다.(페이징 횟수가 늘어나고 캐시 적중률이 떨어짐)
(본문 길이가 굉장히 짧은 인라인 함수의 경우(함수 호출문보다 짧은 경우) 오히려 코드를 더 작게 만들 수 도 있다.)

inline을 명시적으로 컴파일러에게 요청할 수도 있으며, 암시적으로 요청할 수도 있다.
class Person{
public:
// ...
int age() const{ return theAge; } //
// ...
private:
int theAge;
}
위와 같이 클래스 정의 안에 함수를 바로 정의해 놓는 경우(멤버 함수)
컴파일러는 해당 함수를 인라인 함수 후보로 사용한다.
프렌즈 함수도 클래스 내부에서 정의 될 수 있다.

inline 키워드를 사용하면, 명시적으로 인라인 함수를 선언할 수 있다.
// <algorithm> std::max
template <typename T>
inline const T& std::max(const T& a, const T& b){ return a<b? b: a; }
컴파일러에 따라서 inline 처리를 언제하는가는 다르지만, 대부분의 컴파일러는 컴파일 과정에서 이를 수행한다.
(컴파일러에 따라서 링크 타임 혹은 런타임에 inline을 수행하는 컴파일러도 존재한다.)

inline은 컴파일러 선에서 무시할 수 있는 요청이다.
컴파일러는 루프나 재귀함수 등의 복잡한 함수에 대해서는 인라인 확장을 수행하지 않으며,
가상 함수 호출 또한 인라인 확장을 수행하지 않는다.

virtual 키워드는 어떤 함수를 호출할 지, 결정되지 않았음을 명시하는 키워드이다.
inline 키워드는 함수 호출 위치에 호출된 함수를 끼워 넣는 작업을 프로그램 실행 전에 하겠다는 의미로
모순된 두 키워드는 동시에 사용될 수 없다.
inline void f(){ /* ... */ } //
void (*pf)()=f;
// ...
f(); //
pf(); //
또한 인라인 함수의 주소를 취하는 코드가 존재할 경우, 해당 코드를 위해 아웃라인 함수 본문을 만든다.
(함수 포인터를 통해 해당 함수를 호출하는 경우도 포함)
class Base{
public:
// ...
private:
std::string bm1, bm2;
};
class Derived: public Base{
public:
Derived(){}
// ...
private:
std::string dm1, dm2, dm3;
};
C++은 객체를 생성하는 도중 예외가 던져지더라도, 이미 생성이 완료된 부분까지는 소멸을 보장한다.
위처럼 비어있는 것처럼 보이는 Derived() 생성자도 컴파일러는 아래와 같이 구현하게 된다.
//
Derived::Derived(){ //
Base::Base(); // Base
try{ dm1.std::string::string(); } // dm1
catch(...){
Base::~Base(); //
throw;
}
try{ dm2.std::string::string(); } // dm2
catch(...){
dm1.std::string::~string(); // dm1
Base::~Base(); //
throw;
}
try{ dm3.std::string::string(); } // dm3
catch(...){
dm1.std::string::~string(); // dm1
dm2.std::string::~string(); // dm2
Base::~Base(); //
throw;
}
}
생성자와 소멸자는 인라인 하지 않는 것이 좋다.

라이브러리의 인라인 함수를 사용하여 컴파일 한 후, 해당 인라인 함수가 변경될 경우,
사용자는 새로 컴파일을 해야 한다.-라이브러리 차원에서 바이너리 업그레이드 제공할 수 없음
(동적 라이브러리는 함수가 변경되어도 지장 없이 사용할 수 있다.)
따라 인라인 함수를 작성할 때는 영향을 많이 고민해서 설계해야 한다.

인라인 함수에 대한 디버깅 작업은 매우 까다롭다.
함수 인라인은 함수의 크기가 작고, 자주 호출되는 함수에 대해서만 하는 것이 좋다.